﻿using System.ComponentModel;
using CCLog;
using CommonEntity.CommonImageEntities;
using CommonEntity.Handlers;
using OpenCvSharp;
using ScienceAppService.Entities;
using ScienceAppService.ModuleInterface;
using ScienceAppService.RpcUtils;
using ScienceRpcServe.Protos;
using ScienceAppService.Utils;
using Point = OpenCvSharp.Point;
using Point3d = CommonEntity.CommonImageEntities.Point3d;
using Newtonsoft.Json.Linq;

namespace ScienceAppService.ModuleService;
public class MarkDrawService : IMarkDrawService
{

    #region
    private ArmBoneSeg.ArmBoneSegClient _rpcClient;

    private ILoggerService _logger;
    #endregion

    /// <summary>
    /// 当前选中 标注 或者绘制类型
    /// </summary>
    public SegToolsEnum CurrentMarkDrawType { get; private set; }
    public IList<Point3d> UlnaMarkPoints { get; private set; }
    public IList<Point3d> RadiusMarkPoints { get; private set; }
    public IList<Point3d> TfccMarkPoints { get; private set; }
    public int BrushThickness { get; private set; }

    #region event

    public event CommonHandler<IList<Point3d>>? FrushMarksSubMenuEvent;

    public event CommonHandler<string>? AlarmEvent;

    public event CommonHandler<object> FrushCurrentIndexEvent;

    #endregion

    public MarkDrawService()
    {
        _rpcClient = RpcConfigure.Instance.GetArmBoneSegClient();
        CurrentMarkDrawType = SegToolsEnum.MarkNone;
        UlnaMarkPoints = new List<Point3d>();
        RadiusMarkPoints = new List<Point3d>();
        TfccMarkPoints = new List<Point3d>();
        AppServiceEnv.DrawAxMatDic = new Dictionary<int, Mat>();
        AppServiceEnv.DrawSagMatDic = new Dictionary<int, Mat>();
        AppServiceEnv.DrawCorMatDic = new Dictionary<int, Mat>();
    }

    /// <summary>
    /// 绘制画笔调整
    /// </summary>
    /// <param name="thick"></param>
    /// <returns></returns>
    public Mat? SetBrushThickness(int thick)
    {
        BrushThickness = thick;

        var color = SegMarkColor.NoneColor;
        switch (CurrentMarkDrawType)
        {
            case SegToolsEnum.DrawTFCC:
                color = SegMarkColor.TfccColor;
                break;
            case SegToolsEnum.DrawRADIUS:
                color = SegMarkColor.RadiusColor;
                break;
            case SegToolsEnum.DrawULNA:
                color = SegMarkColor.UlanColor;
                break;
        }

        return AppCvUtil.CreateBorderThicknessMat(thick, color);
    }

    public void SetMarkOrDrawType(SegToolsEnum type)
    {
        CurrentMarkDrawType = type;
    }

    /// <summary>
    /// 图像绘制逻辑
    /// </summary>
    /// <param name="p"></param>
    /// <param name="drawMat"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public Mat? Draw(double[] p, Mat drawMat, ImageOrientationEnum orientation, bool isClean = false)
    {
        var color = SegMarkColor.NoneColor;
        switch (CurrentMarkDrawType)
        {
            case SegToolsEnum.DrawTFCC:
                color = SegMarkColor.TfccColor;
                break;
            case SegToolsEnum.DrawRADIUS:
                color = SegMarkColor.RadiusColor;
                break;
            case SegToolsEnum.DrawULNA:
                color = SegMarkColor.UlanColor;
                break;
        }

        switch (orientation)
        {
            case ImageOrientationEnum.TransversePlane:
                AppServiceEnv.DrawAxMatDic ??= new Dictionary<int, Mat>();
                AppServiceEnv.DrawAxMatDic[(int)p[2]] = isClean ? AppCvUtil.CleanMat(new[] { (int)p[0], (int)p[1] }, drawMat, color, BrushThickness)! 
                    : AppCvUtil.DrawMat(new[] { (int)p[0], (int)p[1] }, drawMat, color, BrushThickness)!;
                return AppServiceEnv.DrawAxMatDic[(int)p[2]];

            case ImageOrientationEnum.SagittalPlane:
                AppServiceEnv.DrawSagMatDic ??= new Dictionary<int, Mat>();
                AppServiceEnv.DrawSagMatDic[(int)p[0]] = isClean ? AppCvUtil.CleanMat(new[] { (int)p[1], (int)p[2] }, drawMat, color, BrushThickness)! 
                    : AppCvUtil.DrawMat(new[] { (int)p[1], (int)p[2] }, drawMat, color, BrushThickness)!;
                return AppServiceEnv.DrawSagMatDic[(int)p[0]];

            case ImageOrientationEnum.CoronalPlane:
                AppServiceEnv.DrawCorMatDic ??= new Dictionary<int, Mat>();
                AppServiceEnv.DrawCorMatDic[(int)p[1]] = isClean ? AppCvUtil.CleanMat(new[] { (int)p[0], (int)p[2] }, drawMat, color, BrushThickness)! : AppCvUtil.DrawMat(new[] { (int)p[0], (int)p[2] }, drawMat, color, BrushThickness)!; ;
                return AppServiceEnv.DrawCorMatDic[(int)p[1]];
        }

        return drawMat;
    }

    /// <summary>
    /// 目前只标记横断面
    /// </summary>
    /// <param name="p"></param>
    /// <returns></returns>
    public Point3d MarkPoint(double[] p)
    {
        var point = new Point3d() { X = p[0], Y = p[1], Z = p[2] , IsSelected = true};
        switch (CurrentMarkDrawType)
        {
            case SegToolsEnum.MarkRADIUS:
                for (var i = 0; i < RadiusMarkPoints.Count; i++)
                {
                    RadiusMarkPoints[i].Number = i + 1;
                    RadiusMarkPoints[i].IsSelected = false;
                }
                point.Number = RadiusMarkPoints.Count + 1;
                RadiusMarkPoints.Add(point);
                FrushMarksSubMenuEvent?.Invoke(RadiusMarkPoints);
                break;
            case SegToolsEnum.MarkTFCC:
                for (var i = 0; i < TfccMarkPoints.Count; i++)
                {
                    TfccMarkPoints[i].Number = i + 1;
                    TfccMarkPoints[i].IsSelected = false;
                }
                point.Number = TfccMarkPoints.Count + 1;
                TfccMarkPoints.Add(point);
                FrushMarksSubMenuEvent?.Invoke(TfccMarkPoints);
                break;
            case SegToolsEnum.MarkULNA:
                for (var i = 0; i < UlnaMarkPoints.Count; i++)
                {
                    UlnaMarkPoints[i].Number = i + 1;
                    UlnaMarkPoints[i].IsSelected = false;
                }
                point.Number = UlnaMarkPoints.Count + 1;
                UlnaMarkPoints.Add(point);
                FrushMarksSubMenuEvent?.Invoke(UlnaMarkPoints);
                break;
            default:
                break;
        }
        return point;
    }

    /// <summary>
    /// 翻页时
    /// </summary>
    /// <param name="index"></param>
    /// <param name="orientation"></param>
    /// <returns></returns>
    public Mat? GetMarkMat(int index, ImageOrientationEnum orientation)
    {
        var listP = new List<double[]>();
        var radiusP = new List<Point3d>();
        var ulnaP = new List<Point3d>();
        var tfccP = new List<Point3d>();

        switch (orientation)
        {
            case ImageOrientationEnum.TransversePlane:
                radiusP = RadiusMarkPoints.Where(p => (int)p.Z == index).ToList();
                ulnaP = UlnaMarkPoints.Where(p => (int)p.Z == index).ToList();
                tfccP = TfccMarkPoints.Where(p => (int)p.Z == index).ToList();
                break;
            case ImageOrientationEnum.SagittalPlane:
                radiusP = RadiusMarkPoints.Where(p => (int)p.X == index).ToList();
                ulnaP = UlnaMarkPoints.Where(p => (int)p.X == index).ToList();
                tfccP = TfccMarkPoints.Where(p => (int)p.X == index).ToList();
                break;
            case ImageOrientationEnum.CoronalPlane:
                radiusP = RadiusMarkPoints.Where(p => (int)p.Y == index).ToList();
                ulnaP = UlnaMarkPoints.Where(p => (int)p.Y == index).ToList();
                tfccP = TfccMarkPoints.Where(p => (int)p.Y == index).ToList();
                break;
        }

        var markColor = SegMarkColor.NoneColor;
        switch (CurrentMarkDrawType)
        {
            case SegToolsEnum.MarkRADIUS:
                markColor = SegMarkColor.RadiusColor;
                if (radiusP?.Count > 0)
                {
                    listP.AddRange(radiusP.Select(p => new[] { p.X, p.Y, p.Z, p.IsSelected ? 255 : 100 }));
                }
                break;
            case SegToolsEnum.MarkULNA:
                markColor = SegMarkColor.UlanColor;
                if (UlnaMarkPoints?.Count > 0)
                {
                    listP.AddRange(ulnaP.Select(p => new[] { p.X, p.Y, p.Z, p.IsSelected ? 255 : 100 }));
                }
                break;
            case SegToolsEnum.MarkTFCC:
                markColor = SegMarkColor.TfccColor;
                if (TfccMarkPoints?.Count > 0)
                {
                    listP.AddRange(tfccP.Select(p => new[] { p.X, p.Y, p.Z , p.IsSelected ? 255 : 100 }));
                }
                break;
        }

        if (listP.Count == 0) return null;

        var markMat = new Mat();
        switch (orientation)
        {
            case ImageOrientationEnum.TransversePlane:
                markMat = new Mat(AppServiceEnv.ImageInfo!.Height, AppServiceEnv.ImageInfo.Width, MatType.CV_8UC4, new Scalar(0, 0, 0, 0));
                break;
            case ImageOrientationEnum.SagittalPlane:
                markMat = new Mat((int)(AppServiceEnv.ImageInfo.Count * AppServiceEnv.ImageInfo.ImageFactor), AppServiceEnv.ImageInfo!.Height, MatType.CV_8UC4,
                    new Scalar(0, 0, 0, 0));
                break;
            case ImageOrientationEnum.CoronalPlane:
                markMat = new Mat((int)(AppServiceEnv.ImageInfo.Count * AppServiceEnv.ImageInfo.ImageFactor), AppServiceEnv.ImageInfo!.Width, MatType.CV_8UC4,
                    new Scalar(0, 0, 0, 0));
                break;
        }

        foreach (var p in listP)
        {
            var y = (int)(p[2] * AppServiceEnv.ImageInfo.ImageFactor);
            switch (orientation)
            {
                case ImageOrientationEnum.TransversePlane:
                    Cv2.Line(markMat, new Point((int)(p[0] - 5), (int)(p[1] - 5)), new Point((int)(p[0] + 5), (int)(p[1] + 5)),
                        new Scalar(markColor.B, markColor.G, markColor.R, p[3]), 2);
                    Cv2.Line(markMat, new Point((int)(p[0] - 5), (int)(p[1] + 5)), new Point((int)(p[0] + 5), (int)(p[1] - 5)),
                        new Scalar(markColor.B, markColor.G, markColor.R, p[3]), 2);
                    continue;
                case ImageOrientationEnum.SagittalPlane:
                    Cv2.Line(markMat, new Point((int)(p[1] - 5), (int)(y - 5)), new Point((int)(p[1] + 5),
                        (int)(y + 5)), new Scalar(markColor.B, markColor.G, markColor.R, p[3]), 2);
                    Cv2.Line(markMat, new Point((int)(p[1] - 5), (int)(y + 5)), new Point((int)(p[1] + 5),
                        (int)(y - 5)), new Scalar(markColor.B, markColor.G, markColor.R, p[3]), 2);
                    continue;
                case ImageOrientationEnum.CoronalPlane:
                    Cv2.Line(markMat, new Point((int)(p[0] - 5), (int)(y - 5)), new Point((int)(p[0] + 5),
                        (int)(y + 5)), new Scalar(markColor.B, markColor.G, markColor.R, p[3]), 2);
                    Cv2.Line(markMat, new Point((int)(p[0] - 5), (int)(y + 5)), new Point((int)(p[0] + 5),
                        (int)(y - 5)), new Scalar(markColor.B, markColor.G, markColor.R, p[3]), 2);
                    continue;
            }
        }
        
        return markMat;
    }

    public IList<Point3d> GetMarkPointsBySegType(SegToolsEnum type)
    {
        throw new NotImplementedException();
    }

    /// <summary>
    /// 获取标记的靶点列表
    /// </summary>
    /// <param name="num"> 页码 </param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public IList<Point3d>? GetMarkPoints(int num)
    {
        var rs = new List<Point3d>();
        switch (CurrentMarkDrawType)
        {
            case SegToolsEnum.MarkNone:
                return rs;

            case SegToolsEnum.MarkULNA:
                if (UlnaMarkPoints?.Count > 0 && UlnaMarkPoints?.Count <= num * 5) return null;
                rs = UlnaMarkPoints.OrderBy(mark => mark.Number).Skip(5 * num).ToList();
                break;

            case SegToolsEnum.MarkRADIUS:
                if (RadiusMarkPoints?.Count > 0 && RadiusMarkPoints?.Count <= num * 5) return null;
                rs = RadiusMarkPoints.OrderBy(mark => mark.Number).Skip(5 * num).ToList();
                break;

            case SegToolsEnum.MarkTFCC:
                if (TfccMarkPoints?.Count > 0 && TfccMarkPoints?.Count <= num * 5) return null;
                rs = TfccMarkPoints.OrderBy(mark => mark.Number).Skip(5 * num).ToList();
                break;
        }

        switch (rs.Count)
        {
            case >= 5:
                return rs.Take(5).ToArray();
            case > 0:
            {
                var rsNew = new List<Point3d>();
                for (var i = 0; i < rs.Count; i++)
                {
                    rsNew.Add(rs[i]);
                }

                for (var i = rs.Count; i < 5; i++)
                {
                    rsNew.Add(new Point3d(){Number = -1});
                }

                return rsNew;
            }
            default:
                return rs;
        }
    }

    /// <summary>
    /// 菜单栏选中标记点
    /// </summary>
    /// <param name="num"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public Point3d ChooseMarkPoint(int num)
    {
        var point = new Point3d();

        foreach (var p in RadiusMarkPoints)
        { 
            p.IsSelected = false;
        }

        foreach (var p in TfccMarkPoints)
        {
            p.IsSelected = false;
        }

        foreach (var p in UlnaMarkPoints)
        {
            p.IsSelected = false;
        }

        switch (CurrentMarkDrawType)
        {
            case SegToolsEnum.MarkRADIUS:
                point = RadiusMarkPoints.FirstOrDefault(p => p.Number == num)!;
                break;
            case SegToolsEnum.MarkTFCC:
                point = TfccMarkPoints.FirstOrDefault(p => p.Number == num)!;
                break;
            case SegToolsEnum.MarkULNA:
                point = UlnaMarkPoints.FirstOrDefault(p => p.Number == num)!;
                break;
        }

        point.IsSelected = true;
        return point;
    }

    /// <summary>
    /// 删除标记点
    /// </summary>
    public Point3d? DeleteMarkPoint()
    {

        Point3d en = null;
        switch (CurrentMarkDrawType)
        {
            case SegToolsEnum.MarkRADIUS:
                en = RadiusMarkPoints.FirstOrDefault(p => p.IsSelected)!;
                RadiusMarkPoints.Remove(en);
                FrushMarksSubMenuEvent?.Invoke(RadiusMarkPoints);
                break;
            case SegToolsEnum.MarkTFCC:
                en = TfccMarkPoints.FirstOrDefault(p => p.IsSelected)!;
                TfccMarkPoints.Remove(en);
                FrushMarksSubMenuEvent?.Invoke(TfccMarkPoints);
                break;
            case SegToolsEnum.MarkULNA:
                en = UlnaMarkPoints.FirstOrDefault(p => p.IsSelected)!;
                UlnaMarkPoints.Remove(en);
                FrushMarksSubMenuEvent?.Invoke(UlnaMarkPoints);
                break;
        }

        return en;
    }

    /// <summary>
    /// 重新加载病人的时候使用
    /// </summary>
    public void ClearData()
    {
        UlnaMarkPoints = new List<Point3d>();
        RadiusMarkPoints = new List<Point3d>();
        TfccMarkPoints = new List<Point3d>();
        AppServiceEnv.DrawAxMatDic = new Dictionary<int, Mat>();
        AppServiceEnv.DrawSagMatDic = new Dictionary<int, Mat>();
        AppServiceEnv.DrawCorMatDic = new Dictionary<int, Mat>();

        switch (CurrentMarkDrawType)
        {
            case SegToolsEnum.MarkRADIUS:
                FrushMarksSubMenuEvent?.Invoke(RadiusMarkPoints);
                break;
            case SegToolsEnum.MarkTFCC:
                FrushMarksSubMenuEvent?.Invoke(TfccMarkPoints);
                break;
            case SegToolsEnum.MarkULNA:
                FrushMarksSubMenuEvent?.Invoke(UlnaMarkPoints);
                break;
        }
    }

    /// <summary>
    /// Ai 自动分割
    /// </summary>
    /// <returns></returns>
    public async Task AiSeg()
    {
        // 先进行文件上传
        var rs = await _rpcClient.UploadImageFileAsync(new EmptyRequest());
        if (!rs.IsSuccess)
        {
            AlarmEvent?.Invoke("Origin Image Upload Fail...");
            return;
        }

        var request = new SegRequest();

        if (UlnaMarkPoints?.Count > 0)
        {
            for (var i = 0; i < UlnaMarkPoints.Count; i++)
            {
                request.UlnaPoints.Add(new ScienceRpcServe.Protos.Point3d() 
                {
                    X = UlnaMarkPoints[i].X ,
                    Y = UlnaMarkPoints[i].Y ,
                    Z = UlnaMarkPoints[i].Z ,
                });
            }
        }

        if (RadiusMarkPoints?.Count > 0)
        {
            for (var i = 0; i < RadiusMarkPoints.Count; i++)
            {
                request.RadiusPoints.Add(new ScienceRpcServe.Protos.Point3d()
                {
                    X = RadiusMarkPoints[i].X,
                    Y = RadiusMarkPoints[i].Y,
                    Z = RadiusMarkPoints[i].Z,
                });
            }
        }

        if (TfccMarkPoints?.Count > 0)
        {
            for (var i = 0; i < TfccMarkPoints.Count; i++)
            {
                request.TfccPoints.Add(new ScienceRpcServe.Protos.Point3d()
                {
                    X = TfccMarkPoints[i].X,
                    Y = TfccMarkPoints[i].Y,
                    Z = TfccMarkPoints[i].Z,
                });
            }
        }

        if (request.TfccPoints?.Count == 0 && request.RadiusPoints?.Count == 0 && request.UlnaPoints?.Count == 0) return;
        await _rpcClient.SegBoneAsync(request);
        FrushCurrentIndexEvent?.Invoke(null);
    }

    /// <summary>
    /// 保存勾画信息
    /// </summary>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public async Task SaveDrawMat()
    {
        // 横断面
        var request = new BuildDrawRequest();

        if (AppServiceEnv.DrawAxMatDic.Keys.Count > 0)
        {
            request.Orientation = ImageOrientation.TransversePlane;
            foreach (var kp in AppServiceEnv.DrawAxMatDic)
            {
                var ax = new MaskMat();
                ax.Index = kp.Key;
                var greyMat = new Mat();
                Cv2.CvtColor(kp.Value, greyMat, ColorConversionCodes.BGR2GRAY);
                ax.DrawMat = greyMat.ConvertToByteString();
                request.MaskMats.Add(ax);
            }
            var rs = await _rpcClient.BuildDraw3dAsync(request);
            if (rs.IsSuccess) AppServiceEnv.DrawAxMatDic = new Dictionary<int, Mat>();
        }

        // 矢状面
        if (AppServiceEnv.DrawSagMatDic.Keys.Count > 0)
        {
            request.Orientation = ImageOrientation.SagittalPlane;
            foreach (var kp in AppServiceEnv.DrawSagMatDic)
            {
                var sag = new MaskMat();
                sag.Index = kp.Key;
                var greyMat = new Mat();
                Cv2.CvtColor(kp.Value, greyMat, ColorConversionCodes.BGR2GRAY);
                sag.DrawMat = AppCvUtil.ResizeLabelMat(greyMat, ((double)greyMat.Height) / AppServiceEnv.ImageInfo.ImageFactor).ConvertToByteString();
                request.MaskMats.Add(sag);
            }
            var rs = await _rpcClient.BuildDraw3dAsync(request);
            if (rs.IsSuccess) AppServiceEnv.DrawSagMatDic = new Dictionary<int, Mat>();
        }

        // 冠状面
        if (AppServiceEnv.DrawCorMatDic.Keys.Count > 0)
        {
            request.Orientation = ImageOrientation.CoronalPlane;
            foreach (var kp in AppServiceEnv.DrawCorMatDic)
            {
                var sag = new MaskMat();
                sag.Index = kp.Key;
                var greyMat = new Mat();
                Cv2.CvtColor(kp.Value, greyMat, ColorConversionCodes.BGR2GRAY);
                sag.DrawMat = AppCvUtil.ResizeLabelMat(greyMat, ((double)greyMat.Height) / AppServiceEnv.ImageInfo.ImageFactor).ConvertToByteString();
                request.MaskMats.Add(sag);
            }
            var rs = await _rpcClient.BuildDraw3dAsync(request);
            if (rs.IsSuccess) AppServiceEnv.DrawCorMatDic = new Dictionary<int, Mat>();
        }


    }
}
